#ifndef TAB_DELIMITED_FILE_TO_MAP_H
#define TAB_DELIMITED_FILE_TO_MAP_H

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <cassert>
#include <stdexcept>
#include <boost/tuple/tuple.hpp>
#include "index_string.h"
#include "read_file_lines.h"

#include "print_error.h"




struct t_field_extractor
{
	std::vector<unsigned>		field_indices;
	std::vector<std::string>	concatenators;
	std::string					terminal_str;
	t_field_extractor(std::string format_str);
};

std::ostream& operator << (std::ostream& os, const t_field_extractor& field_extrator);
void prepare_field_extractor(std::string format_string, t_field_extractor& field_extractor);


template<typename ERROR_CALLBACK, typename CONT>
std::string construct_string_from_fields(const CONT& fields,
									const t_field_extractor& field_extractor,
									unsigned curr_line_cnt,
									ERROR_CALLBACK& report_error)
{
	std::string final_string;
	for (unsigned i = 0; i < field_extractor.field_indices.size(); ++i)
	{
		final_string += field_extractor.concatenators[i];
		if (field_extractor.field_indices[i] < fields.size())
			final_string += fields[field_extractor.field_indices[i]];
		else
			report_error(curr_line_cnt, fields.size(), field_extractor.field_indices[i]);
	}
	final_string += field_extractor.terminal_str;
	return final_string;
}


//________________________________________________________________________________________

//		t_identifier_map

//________________________________________________________________________________________
struct t_identifier_map
{
	struct t_fields_error
	{
		std::string field_name;
		t_fields_error(const std::string& field_name_):field_name(field_name_){}
		void operator()(unsigned line_cnt, unsigned fields_cnt, unsigned field_err);
	};
	struct t_1_to_m
	{
		unsigned	offset;
		unsigned	end;
		t_1_to_m(unsigned o, unsigned e): offset(o), end(e){}
	};

	t_index_string&				ids_from;
	t_index_string&				ids_to;
	//std::deque<std::deque<unsigned> >&	id_map;
	std::deque<std::pair<unsigned, unsigned> > id_pairs;
	std::string					comment_letters;
	unsigned					cnt_ignored_entries;
	unsigned					cnt_entries;
	unsigned					cnt_lines;
	t_field_extractor 			from_format_field;
	t_field_extractor 			to_format_field;
	t_fields_error				from_error;
	t_fields_error				to_error;
	t_identifier_map(	t_index_string&				ids_from_,
						t_index_string&				ids_to_,
						const std::string&			comment_letters_,
						const std::string&			from_format_string,
						const std::string&			to_format_string);
	void operator()(std::istream& is, unsigned cnt_line, const std::string& line);
	void parse_file(const std::string& file_name, std::ostream& VERBOSE)
	{
		read_file_lines(*this, file_name, VERBOSE, '\n');
	}
	typedef std::vector<unsigned> 				t_vecu;
	
	unsigned create_lookups(std::deque<t_1_to_m>&	lookup_1_to_m,
							std::deque<unsigned>&	many_elements);
	void single_linkage(std::deque<boost::tuple<unsigned,  unsigned> >&	clusters_1_to_1,
						std::deque<boost::tuple<unsigned,  t_vecu> >&	clusters_1_to_m,
						std::deque<boost::tuple<t_vecu, unsigned> >&	clusters_m_to_1,
						std::deque<boost::tuple<t_vecu, t_vecu> >&		clusters_m_to_m);
};


//________________________________________________________________________________________
#endif //TAB_DELIMITED_FILE_TO_MAP_H
